﻿using System;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Threading;
using System.Windows.Forms;

namespace Updater
{
    public partial class Main : Form
    {
        private updateItem app;
        //
        private delegate void workerThreadProgressChangedDelegate(object sender, string nextAction);

        public Main(string[] args)
        {
            InitializeComponent();
            //
            Log.setPath("//logs//Updater");
            Log.Info("Loading...");
            app = new updateItem();
            for (int i = 0; i != args.Length; ++i)
            {
                switch (args[i])
                {
                    case "-id":
                    {
                        try { app.appId = Convert.ToInt32(args[i + 1]); Log.Info("AppID: " + app.appId); }
                        catch (Exception) { MessageBox.Show("Bad ID"); Environment.Exit(0); Log.Error("Bad ID"); }
                        break;
                    }
                    case "-version":
                        app.setVersion(args[i + 1]); Log.Info("Local Version: " + app.appVersion); break;
                }
            }
        }

        private void Main_Load(object sender, EventArgs e)
        {
            mainWorker workerThread = new mainWorker(app);
            workerThread.progressChange += new mainWorker.progressHandler(workerThread_progressChange);
            workerThread.run();
            Log.Debug("Main thread started");
        }

        private void workerThread_progressChange(object sender, string nextAction)
        {
            if (InvokeRequired)
            {
                BeginInvoke(new workerThreadProgressChangedDelegate(workerThread_progressChange), new object[] { sender, nextAction });
                return;
            }
            //
            progressBar1.PerformStep();
            if (!String.IsNullOrEmpty(nextAction)) { label2.Text = nextAction; }
            Log.Info(nextAction);
            if (nextAction == "finished") { this.Close(); }
        }

        private void Main_FormClosing(object sender, FormClosingEventArgs e)
        {
            Environment.Exit(0);   
        }
    }

    #region Main Worker

    public class mainWorker
    {
        private string updateServer = "http://updater.tlokzz.info/";
        private string downloadTo   =  Environment.GetEnvironmentVariable("TEMP") + "\\";
        private string remoteVersion;
        private int waitTime = 200;
        private updateItem localApp;
        //
        public delegate void progressHandler(object sender, string nextAction);
        public event progressHandler progressChange;

        public mainWorker(updateItem app)
        {
            this.localApp = app;
        }

        public void run()
        {
            new Thread(new ParameterizedThreadStart(workerThread)).Start();
            Log.Debug("Worker thread started");
        }

        private void workerThread(object obj)
        {
            Log.Divider("Main Script");
            Thread.Sleep(waitTime);
            progressChange(this, "downloading update file");
            if (!downloadFile(updateServer + "UpdateFiles/uid_" + localApp.appId.ToString() + ".ini", downloadTo + "uid_" + localApp.appId + ".ini"))
            {
                MessageBox.Show("Error downloading update file. Please try again later. If this problem persists please contact the developer.");
                Environment.Exit(0);
            }
            //
            Thread.Sleep(waitTime);
            progressChange(this, "reading update file");
            IniFile ini = new IniFile(downloadTo + "uid_" + localApp.appId + ".ini");
            localApp.appName = ini.GetString("UpdateInfo", "AppName", "");
            Log.Info("AppName:" + localApp.appName);
            remoteVersion = ini.GetString("UpdateInfo", "LatestVersion", "");
            Log.Info("LatestVersion:" + remoteVersion);
            //
            Thread.Sleep(waitTime);
            progressChange(this, "comparing versions");
            if (new Version(remoteVersion).CompareTo(localApp.appVersion) == 1)
            {
                Thread.Sleep(waitTime);
                progressChange(this, "update found... sending kill switch to application");
                foreach (Process proc in Process.GetProcessesByName(localApp.appName))
                {
                    if (!proc.HasExited) { proc.Kill(); Log.Debug("Killed app: " + proc.StartInfo.FileName); }
                    Thread.Sleep(100);
                }
                Thread.Sleep(waitTime);
                progressChange(this, "downloading " + localApp.appName + " version: " + remoteVersion);
                //
                if (!downloadFile(updateServer + "Programs/" + localApp.appName + ".exe", Application.StartupPath + "\\" + localApp.appName + ".exe"))
                {
                    MessageBox.Show("Error downloading " + localApp.appName + " v" + remoteVersion + ". Please try again later. If this problem persists please contact the developer.");
                }
            }
            else { Thread.Sleep(waitTime); progressChange(this, "no update available"); }
            //
            Thread.Sleep(waitTime);
            progressChange(this, "cleaning up");
            File.Delete(downloadTo + "uid_" + localApp.appId + ".ini");
            //
            Thread.Sleep(waitTime);
            progressChange(this, "finished");
        }

        private bool downloadFile(string path, string downTo)
        {
            try
            {
                WebRequest request = WebRequest.Create(new Uri(path));
                request.Method = "HEAD";
                HttpWebResponse response = (HttpWebResponse)request.GetResponse();
                if (response.StatusCode == HttpStatusCode.OK)
                {
                    new WebClient().DownloadFile(path, downTo);
                    return true;
                }
                return false;
            }
            catch (Exception e)
            {
                Log.Error(e.Message);
                return false; 
            }
        }
    }

    #endregion

    #region Update Item

    public class updateItem
    {
        int    _appId;
        string _appName;
        Version _appVersion;

        public updateItem() { }

        public updateItem(int appId_, string appVersion_)
        {
            this._appId      = appId_;
            this._appVersion = new Version(appVersion_);
        }

        public int appId
        {
            get { return this._appId; }
            set { this._appId = value; }
        }

        public string appName
        {
            get { return this._appName; }
            set { this._appName = value; }
        }

        public Version appVersion
        {
            get { return this._appVersion; }
        }

        public void setVersion(string appVersion_)
        {
            this._appVersion = new Version(appVersion_);
        }
    }

    #endregion
}
